package cn.toolck.my.spring.factory;

import cn.toolck.my.spring.annotation.*;
import cn.toolck.my.spring.exception.BeanCreationException;
import cn.toolck.my.spring.pojo.BeanDefinition;
import cn.toolck.my.spring.utils.FileUtils;

import java.lang.reflect.Field;
import java.util.*;

public class BeanFactory {
    private Map<String, Object> beanMap = new HashMap<>();
    private Map<String, Object> creatingBeanMap = new HashMap<>();
    private Map<String, BeanDefinition> definitionMap = new HashMap<>();

    public BeanFactory() {
    }

    public BeanFactory(Class<?> configClass) {
        parsingConfig(configClass);
    }


    /**
     * @param name beanName
     * @return 返回已经创建好的bean对象
     * @throws Exception
     */
    public Object getBean(String name) {
        // 先从容器中的beanMap中进行查找
        Object bean = beanMap.get(name);
        if (bean != null) {
            return bean;
        }
        // 在从容器中的正在创建的缓存（creatingBeanMap）中进行查找
        bean = creatingBeanMap.get(name);
        if (bean != null) {
            return bean;
        }
        try {
            // 都没有则进行创建
            bean = creatBean(name);
            beanMap.put(name, bean);
            // 创建完成后进行删除相应的creatingBeanMap中的对象
            if (creatingBeanMap.containsKey(name)) {
                creatingBeanMap.remove(name);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

    /**
     * @param name bean的name
     * @return 创建的bean
     * @throws Exception 进行bean的创建
     */
    private Object creatBean(String name) {
        BeanDefinition beanDefinition = definitionMap.get(name);
        if (beanDefinition == null) {
            throw new BeanCreationException(name, "找不到这个BeanDefinition！");
        }
        Class<?> beanClass = beanDefinition.getClassName();
        // 进行实例化
        try {
            Object bean = beanClass.getConstructor().newInstance();
            List<String> autoFieldName = beanDefinition.getAutoFieldName();
            if (autoFieldName.isEmpty()) {
                return bean;
            }
            // 如果字段中有依赖于其他的bean的时候先进行缓存实例化的对象之后再进行初始化。
            creatingBeanMap.put(name, bean);
            for (String fieldName : autoFieldName) {
                Field field = beanClass.getDeclaredField(fieldName);
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                field.set(bean, getBean(fieldName));
            }
            Transactional transactional = beanDefinition.getTransactional();
            if (transactional != null) {
                ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
                bean = proxyFactory.getProxyObject(bean);
            }
            return bean;
        } catch (Exception e) {
            throw new BeanCreationException(e);
        }
    }

    /**
     * 解析配置类并生成相应的beanDefinition缓存在BeanFactory中
     *
     * @param configClass 配置类
     */
    public void parsingConfig(Class<?> configClass) {
        try {
            ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
            Class[] classes = componentScan.basePackageClasses();
            Set<Class> classSet = new HashSet<Class>(Arrays.asList(classes));
            String[] packages = componentScan.basePackages();
            List<Class> baseClasses = FileUtils.scanPackage(packages);
            classSet.addAll(baseClasses);
            for (Class<?> componentClass : classSet) {
                // 解析注解并将所有的信息封装为BeanDefinition对象
                if (componentClass.isAnnotationPresent(Component.class) || componentClass.isAnnotationPresent(Service.class)) {
                    BeanDefinition beanDefinition = AnnotationParse.parseComponentAnno(componentClass);
                    String name = beanDefinition.getName();
                    if (!definitionMap.containsKey(name)) {
                        definitionMap.put(name, beanDefinition);
                    }
                }
            }
            Set<String> beanNames = definitionMap.keySet();
            // 进行实例化与初始化
            for (String beanName : beanNames) {
                getBean(beanName);
            }
        } catch (BeanCreationException e) {
            throw e;
        } catch (Exception e) {
            throw new BeanCreationException(e);
        }

    }
}